﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Rappelz.GameServer
{
    public class RespawnObject : BaseObject, IMonsterDeleteHandler
    {
        public RespawnObject(GameContent.MonsterRespawnInfo rh)
        {
//            ArSchedulerObject::ArSchedulerObject(&this->baseclass_0);
//            v3->baseclass_40.vfptr = &StructMonster::MonsterDeleteHandler::_vftable_;
//            v3->baseclass_0.vfptr = &RespawnObject::_vftable_;
//            v3->baseclass_40.vfptr = &RespawnObject::_vftable_;

            this.info = new MonsterRespawnInfo(rh);

//            XCriticalSection::XCriticalSection(&v3->m_CS, "RespawnObject::m_CS");
            Interlocked.Increment(ref GameContent.g_nRespawnObjectCnt);
            this.m_nMaxRespawnNum = this.info.prespawn_count;
            this.lastDeadTime = 0;
        }
// UserDefinedType: RespawnObject
// BaseClass      :   ArSchedulerObject, offset = 0x0
// VTable         :     , Type:  *
// Enum           :     AR_OBJECT_PRIORITY, Type: int
// Function       :     public void ArSchedulerObject(const struct ArSchedulerObject &)
// Function       :     public void ArSchedulerObject::ArSchedulerObject()
// Function       :     public unsigned int ArSchedulerObject::GetHandle()
// Function       :     public void ArSchedulerObject::onProcess(int)
// Function       :     public bool IsEnable()
// Function       :     public void Disable()
// Function       :     public bool IsDeleteRequested()
// Function       :     public volatile enum ArSchedulerObject::AR_OBJECT_PRIORITY GetPriority()
// Function       :     public volatile unsigned int GetPriorityQueueIndex()
// Function       :     public volatile int GetPendingPriority()
// Function       :     public volatile int GetFinalPriority()
// Function       :     public void EnterSpinLock()
// Function       :     public void LeaveSpinLock()
// Function       :     public volatile __int64 GetLastProcTime()
// Function       :     public volatile unsigned int GetLastProcTick()
// Data           :     this+0x8, Member, Type: volatile bool, bIsDeleteRequested
// Data           :     this+0x10, Member, Type: volatile __int64, last_proc_time
// Data           :     this+0x18, Member, Type: volatile unsigned int, last_proc_tick
// Data           :     this+0x1C, Member, Type: volatile enum ArSchedulerObject::AR_OBJECT_PRIORITY, pending_priority
// Data           :     this+0x20, Member, Type: volatile int, pending_thread_index
// Data           :     this+0x24, Member, Type: volatile unsigned int, pending_priority_queue_index
// Data           :     this+0x28, Member, Type: volatile enum ArSchedulerObject::AR_OBJECT_PRIORITY, priority
// Data           :     this+0x2C, Member, Type: volatile int, thread_index
// Data           :     this+0x30, Member, Type: volatile unsigned int, priority_queue_index
// Data           :     this+0x34, Member, Type: volatile int, lock
// Function       :     protected bool ArSchedulerObject::ProcDelete()
// Function       :     protected bool ArSchedulerObject::IsDeleteable()
// Function       :     protected void ArSchedulerObject::~ArSchedulerObject()
// Data           :     this+0x38, Member, Type: volatile bool, m_bIsEnable
// Function       :     public struct ArSchedulerObject & operator=(const struct ArSchedulerObject &)
// Function       :     public void __local_vftable_ctor_closure()
// Function       :     protected void * __vecDelDtor(unsigned int)
// 
// BaseClass      :   StructMonster::MonsterDeleteHandler, offset = 0x40
// VTable         :     , Type:  *

        public void onMonsterDelete(Monster mob)
        {
// .text:0048F184 itErase         = std::_Vector_iterator<unsigned int,std::allocator<unsigned int> > ptr -28h
// .text:0048F184 __scoped_lock   = XScopedLock ptr -18h
            bool bNeedToStartRespawn;           // -13  -0Dh
// .text:0048F184 pMonster        = dword ptr  8
// 
// Data           :   ebp Relative, [00000008], Param, Type: struct StructMonster *, pMonster
// Data           :   ebp Relative, [FFFFFFF3], Local, Type: bool, bNeedToDeleteRoamer
// Data           :   ebp Relative, [FFFFFFEC], Local, Type: class XScopedLock, __scoped_lock
// Data           :   ebp Relative, [FFFFFFE4], Local, Type: class std::_Vector_iterator<StructRoamer::ROAMING_CREATURE_INFO,std::allocator<StructRoamer::ROAMING_CREATURE_INFO> >, it


            if (this.m_vRespawnedMonster.Contains(mob.m_hHandle))
            {
                this.m_vRespawnedMonster.Remove(mob.m_hHandle);

                // locks something, not sure what yet (*(*&this->info.baseclass_0.layer + 4))(&this->info.baseclass_0.layer);

                this.lastDeadTime = Globals.GetArTime();
                bNeedToStartRespawn = this.info.count-- <= this.m_nMaxRespawnNum;
                //             v13 = v3->baseclass_40.vfptr;
                if (this.m_nMaxRespawnNum < this.info.max_num)
                    this.m_nMaxRespawnNum++; ;
                if (bNeedToStartRespawn)
                    RappelzServer.Instance.SetObjectPriority(this, Priority.Normal);
            }
// unlock it            (*(v15 + 12))(v4);

        }
// Function       :     public void MonsterDeleteHandler(const struct StructMonster::MonsterDeleteHandler &)
// Function       :     public void MonsterDeleteHandler()
// Function       :     public struct StructMonster::MonsterDeleteHandler & operator=(const struct StructMonster::MonsterDeleteHandler &)
// 
// Function       :   public void RespawnObject(const struct RespawnObject &)
// Function       :   public void RespawnObject::onMonsterDelete(struct StructMonster *)

        public override void onProcess(int nThreadIdx)
        {
//             RespawnObject *v2; // esi@1
//             XEnvStruct *v3; // eax@1
//             int v4; // edi@1
//             ILockVtbl *v5; // eax@1
//             unsigned int v6; // eax@1
//             volatile unsigned int v7; // ecx@1
//             unsigned int v8; // eax@1
//             unsigned int v9; // ecx@2
//             int v10; // edx@4
//             int v11; // ecx@4
//             bool v12; // cf@4
//             int v13; // ebx@6
//             ArcadiaLock *v14; // ecx@6
//             bool v15; // zf@6
//             double v16; // st7@10
//             int v17; // eax@10
//             double v18; // st7@10
//             StructMonster *v19; // eax@14
//             int v20; // ecx@15
//             unsigned int v21; // eax@20
//             ArcadiaServer *v22; // eax@26
//             ArcadiaServer *v23; // eax@26
//             FileLogHandler *v24; // eax@26
//             FileLogHandlerVtbl *v25; // ecx@26
//             XConsole *v26; // eax@26
//             ArcadiaServer *v27; // eax@28
//             unsigned int v28; // edx@31
//             unsigned int v29; // ecx@35
//             GameObject *v30; // ebx@38
//             ArcadiaServer *v31; // eax@46
//             ArcadiaLock *v32; // eax@46
//             volatile bool v33; // al@46
//             ArcadiaServer *v34; // eax@49
//             int v35; // eax@59
//             ArcadiaAutoLock _lock; // [sp+0h] [bp-4Ch]@46
//             ArcadiaLock result; // [sp+14h] [bp-38h]@46
//             XScopedLock __scoped_lock; // [sp+18h] [bp-34h]@1
//             unsigned int _Val; // [sp+1Ch] [bp-30h]@17
//             unsigned int v40; // [sp+20h] [bp-2Ch]@4
// 
//             int v47; // [sp+48h] [bp-4h]@1
//             ArcadiaLock *buf[64]; // [sp+4Ch] [bp+0h]@1
//             ArcadiaLock *nThreadIdxa; // [sp+158h] [bp+10Ch]@1

//             buf[59] = nThreadIdxa;
//             v2 = this;
//             sprintf(buf, "thread.scheduler.%d.proc");
//             buf[59] = "RespawnObject";
//             buf[58] = buf;
//             v3 = ENV();
//             XEnvStruct::Set(v3, buf[58], buf[59]);
            bool bFirstRegen;
            uint respawn_count;
            int nCountToDelete;
            uint x;
            uint y;
            uint try_cnt;
            SchedulerObject.Priority p = SchedulerObject.Priority.Idle;
            Monster monster = null;

            lock(this)
            {
    //            v47 = 0;
                uint ct = Globals.GetArTime();
    //            v7 = this.info.count;
                try_cnt = ct;
    //            v8 = this.m_nMaxRespawnNum;
                if (this.info.count < this.m_nMaxRespawnNum)
                {
                    Interlocked.Add(ref GameContent.g_nRespawnTryCnt, 1);
                    if (this.lastDeadTime != 0 && this.lastDeadTime + this.info.interval > ct)
                        return;
                    respawn_count = this.info.inc;
                    if (this.info.inc >= this.m_nMaxRespawnNum - this.info.count)
                        respawn_count = this.m_nMaxRespawnNum - this.info.count;

                    bFirstRegen = false;
                    if (this.lastDeadTime == 0)
                    {
                        this.lastDeadTime = ct;
                        respawn_count = this.m_nMaxRespawnNum;
                        bFirstRegen = true;
                    }
                    if (respawn_count > 0 )
                    {
                        try_cnt = 0;
                        nCountToDelete = 0;
                        while (true)
                        {
                            x = (uint)Globals.GetRandomInt32((int)this.info.left, (int)this.info.right);
                            y = (uint)Globals.GetRandomInt32((int)this.info.top, (int)this.info.bottom);
                            if (bFirstRegen)
                            {
                                if (nCountToDelete == 0)
                                {
                                    ++try_cnt;
                                    if ( try_cnt > 0x1F4 )
                                        break;
                                }
                            }

                            monster = null;
                            if (!GameContent.IsBlocked(x,y))
                            {
                                monster = GameContent.respawnMonster(x, y, this.info.layer, this.info.monster_id, this.info.is_wandering, this.info.way_point_id, this, true);
                            }
                            if (monster != null)
                            {
                                if (this.info.dungeon_id != 0)
                                    monster.m_nDungeonId = this.info.dungeon_id;
                                this.m_vRespawnedMonster.Add(monster.m_hHandle);
                            }
                            this.info.count++;
                            nCountToDelete++;
                            if (nCountToDelete >= respawn_count)
                            {
                                break; // goto label_20
                            }
                        }
//                         Interlocked.Add(ref GameContent.g_nDropRespawn, 1);
//                         RappelzServer.Instance.SetObjectPriority(this, BaseObject.ObjectPriority.Idle);
//                         RappelzServer.Instance.DeleteObject(this);
// 
//                         Globals.Log.Error("Unable to respawn monster group. RespawnID({0}), MonsterID({1})", this.info.id, this.info.monster_id);
                        return;
                    }

                    if (this.info.count != this.m_nMaxRespawnNum)
                        return;

                    p = SchedulerObject.Priority.Low;
                    if (this.m_nMaxRespawnNum == 1 || this.m_nMaxRespawnNum == this.info.prespawn_count
                      || this.info.dungeon_id != 0 || this.info.way_point_id != 0)
                        p = SchedulerObject.Priority.Idle;
                    RappelzServer.Instance.SetObjectPriority(this, p);
                    return;
                }

                if (this.info.dungeon_id != 0 || this.info.way_point_id != 0)
                    return;

                if (this.m_nMaxRespawnNum == this.info.prespawn_count)
                {
                    RappelzServer.Instance.SetObjectPriority(this, SchedulerObject.Priority.Idle);
                    return;
                }
                if (this.m_nMaxRespawnNum < this.info.prespawn_count)
                {
                    this.m_nMaxRespawnNum = this.info.prespawn_count;
                    RappelzServer.Instance.SetObjectPriority(this, SchedulerObject.Priority.Normal);
                    return;
                }

                if (this.lastDeadTime == 0 || this.info.interval + this.lastDeadTime + 18000 > ct)
                    return;

                nCountToDelete = (int)(this.m_nMaxRespawnNum - this.info.prespawn_count);
                if (this.m_nMaxRespawnNum == this.info.prespawn_count)
                {
                    RappelzServer.Instance.SetObjectPriority(this, SchedulerObject.Priority.Idle);
                    return;
                }
                while (true)
                {
                    uint h = 0;
                    if(this.m_vRespawnedMonster.Count > 0)
                        h = this.m_vRespawnedMonster.Last();

                    Monster mob = (Monster) GameObject.get(h);
                    
                    if (mob == null || !mob.IsMonster())
                        mob = null;
                    if (mob == null || !ReferenceEquals(mob.m_pDeleteHandler, this))
                    {
                        this.m_nMaxRespawnNum--;
                        this.info.count--;
                        nCountToDelete--;
                        this.m_vRespawnedMonster.Remove(h);
                    }
                    else
                    {
                        if (mob.bIsNearClient)
                            break;

                        this.m_nMaxRespawnNum--;
                        this.info.count--;
                        nCountToDelete--;
                        this.m_vRespawnedMonster.Remove(h);

                        lock(mob)
                        {
                            if (mob.m_bIsEnable)
                            {
                                mob.m_pDeleteHandler = null;
                                mob.m_bIsEnable = false;;
//                                 if (mob.bIsInWorld)
//                                     RemoveMonsterFromWorld(mob);
                                RappelzServer.Instance.DeleteObject(mob);
                            }
                        }
                    }

                    if (nCountToDelete == 0)
                    {
                        RappelzServer.Instance.SetObjectPriority(this, SchedulerObject.Priority.Idle);
                        return;
                    }
                }
                if (nCountToDelete == 0)
                {
                    RappelzServer.Instance.SetObjectPriority(this, SchedulerObject.Priority.Idle);
                    return;
                }
            }
        }

// Function       :   public bool RespawnObject::ProcDelete()
        public MonsterRespawnInfo info;                 // 0x44
        public uint m_nMaxRespawnNum;                               // 0x80
        public List<uint> m_vRespawnedMonster = new List<uint>();   // 0x84
        public uint lastDeadTime;                                   // 0x94
// Data           :   this+0x98, Member, Type: class XCriticalSection, m_CS

    }

    public class MonsterRespawnInfo : GameContent.MonsterRespawnInfo
    {
        public MonsterRespawnInfo(GameContent.MonsterRespawnInfo info)
            : base(info)
        {
            count = 0;
            prespawn_count = (uint)((float)info.max_num * GameRule.fFieldSpawnMultiply);
            if(prespawn_count < 1 || dungeon_id != 0 || this.way_point_id != 0)
                prespawn_count = info.max_num;
        }

        public uint count;
        public uint prespawn_count;
    }


}
